#include "glwidget.h"

#include <QDebug>

#include <QtMath>

GLWidget::GLWidget(QWidget *parent):QOpenGLWidget(parent)//,
//    m_triangleVBO(QOpenGLBuffer::VertexBuffer),
//    m_triangleEBO(QOpenGLBuffer::IndexBuffer)
{
    learnTestProjectionMatrix();

    m_pColorFuncTexBuf = nullptr;

    m_glviewport.setX(0);
    m_glviewport.setY(0);
    m_glviewport.setWidth(1.0);
    m_glviewport.setHeight(1.0);
    m_isViewportChanged = true;

    m_camera.verticalAngle = 60.0f;
    resetCamera();

    m_isCamera1stReseted = false;

    m_cameraTool = CAMCT_NONE;

    m_glCommonProgram.program = 0;
    m_glCommonProgram.projViewMat4loc = -1;

    m_triangleCtx.vao = 0;
    m_triangleCtx.vbo = 0;
    m_triangleCtx.ebo = 0;
    m_triangleCtx.texture1 = 0;
    m_triangleCtx.program = 0;
    m_triangleCtx.colorloc = -1;
    m_triangleCtx.vertexCodeFile = ":/GLSL/Triangle/vert";
    m_triangleCtx.fragmentCodeFile = ":/GLSL/Triangle/frag";

    m_volumeCtx.program = 0;
    m_volumeCtx.vao = 0;
    m_volumeCtx.vbo = 0;
    m_volumeCtx.ebo = 0;
    m_volumeCtx.bgDepthTex = 0;
    m_volumeCtx.fbo = 0;
    m_volumeCtx.rbo = 0;
    m_volumeCtx.colortex = 0;
    m_volumeCtx.depthtex = 0;
    m_volumeCtx.objbottomTex = 0;
    m_volumeCtx.obj3d8UC1Tex = 0;
    m_volumeCtx.bgColorTex = 0;
    m_volumeCtx.colormap1dTex = 0;

    m_pColorFuncTexBuf = new float[ 3 * 1024];
    resetVoxelRGBSegmentPoints();
    modifiedVoxelColorFunc();
}

GLWidget::~GLWidget()
{
    if(m_triangleCtx.ebo > 0) glDeleteVertexArrays(1, &m_triangleCtx.ebo);
    if(m_triangleCtx.vbo > 0) glDeleteBuffers(1, &m_triangleCtx.vbo);
    if(m_triangleCtx.texture1 > 0) glDeleteTextures(1,&m_triangleCtx.texture1);
    if(m_triangleCtx.vao > 0) glDeleteBuffers(1, &m_triangleCtx.vao);
    if(m_triangleCtx.program > 0) glDeleteProgram(m_triangleCtx.program);

    if(m_volumeCtx.ebo > 0) glDeleteVertexArrays(1, &m_volumeCtx.ebo);
    if(m_volumeCtx.vbo > 0) glDeleteBuffers(1, &m_volumeCtx.vbo);
    if(m_volumeCtx.vao > 0) glDeleteBuffers(1, &m_volumeCtx.vao);
    if(m_volumeCtx.vbo_fill > 0) glDeleteBuffers(1, &m_volumeCtx.vbo_fill);
    if(m_volumeCtx.bgColorTex > 0) glDeleteTextures(1, &m_volumeCtx.bgColorTex);
    if(m_volumeCtx.objbottomTex > 0) glDeleteTextures(1, &m_volumeCtx.objbottomTex);
    if(m_volumeCtx.depthtex > 0) glDeleteTextures(1, &m_volumeCtx.depthtex);
    if(m_volumeCtx.colortex > 0) glDeleteTextures(1, &m_volumeCtx.colortex);
    if(m_volumeCtx.bgDepthTex > 0) glDeleteTextures(1, &m_volumeCtx.bgDepthTex);
    if(m_volumeCtx.obj3d8UC1Tex > 0) glDeleteTextures(1, &m_volumeCtx.obj3d8UC1Tex);
    if(m_volumeCtx.colormap1dTex > 0) glDeleteTextures(1, &m_volumeCtx.colormap1dTex);
    if(m_volumeCtx.rbo > 0) glDeleteRenderbuffers(1, &m_volumeCtx.rbo);
    if(m_volumeCtx.fbo > 0) glDeleteFramebuffers(1, &m_volumeCtx.fbo);
    if(m_volumeCtx.program > 0) glDeleteProgram(m_volumeCtx.program);


    if(m_glCommonProgram.program > 0) glDeleteProgram(m_glCommonProgram.program);

    if(m_pColorFuncTexBuf){
        delete [] m_pColorFuncTexBuf;
        m_pColorFuncTexBuf = nullptr;
    }

//    makeCurrent();
//    m_triangleEBO.destroy();
//    m_triangleVBO.destroy();
//    m_triangleVAO.destroy();
    //    doneCurrent();
}


void GLWidget::learnTestProjectionMatrix()
{
    QMatrix4x4 m;
    float l=-400,r=400,b=-300,t=300,n=5,f=100;
    qDebug()<<"-------------frustum-------------";
    m.setToIdentity();
     qDebug()<<m(0,0)<<m(1,1)<<m(2,2)<<m(3,3);  //索引是行优先的 但在内部数据存放是列优先的 为了与opengl对应
    m.frustum(l,r,b,t,n,f);
    qDebug()<<m;

    auto v_angle = qAtan(static_cast<qreal>(t/n));
    v_angle = 2.0 * qRadiansToDegrees(v_angle);
//    qDebug()<< v_angle;
    m.setToIdentity();
    m.perspective(static_cast<float>(v_angle),r/t,n,f);     //perspective函数的 aspectRatio 是指宽度除以高度
    qDebug()<< "  perspective :" <<m;

    m.setRow(0,QVector4D(2*n/(r-l),0,(r+l)/(r-l),0));
    m.setRow(1,QVector4D(0,2*n/(t-b),(t+b)/(t-b),0));
    m.setRow(2,QVector4D(0,0,-(f+n)/(f-n),-2*f*n/(f-n)));
    m.setRow(3,QVector4D(0,0,-1,0));
    qDebug()<<m;
    qDebug()<<m*QVector4D(1,2,-5,1);
    qDebug()<<"------------ortho--------------";

    m.setToIdentity();
    m.ortho(-400,400,-300,300,5,100);
    qDebug()<<m;
    m.setRow(0,QVector4D(2/(r-l),0,0,-(r+l)/(r-l)));
    m.setRow(1,QVector4D(0,2/(t-b),0,-(t+b)/(t-b)));
    m.setRow(2,QVector4D(0,0,-2/(f-n),-(f+n)/(f-n)));
    m.setRow(3,QVector4D(0,0,0,1));
    qDebug()<<m;
    qDebug()<<m*QVector4D(1,2,-5,1);
    qDebug()<<"--------------------------";
}

void GLWidget::resetCamera()
{
    //    m_camera.verticalAngle = 90.0f;     //垂直可视开角

    //旋转矩阵
    m_camera.rotateMat.setToIdentity();

    //移动参数
    m_camera.trans = QVector3D(0.0f,0.0f,0.0f);    //移动向量

    //缩放
    m_camera.zoom = 1.0f;

    //透视投影用的参数
    m_camera.aspectRatio = static_cast<float>( m_glviewport.width() / m_glviewport.height() );       //可视平面宽高比

    float width,height,depth = getMainObjMaxSize();
    if(m_camera.aspectRatio>1.0f){              //求出肯定能够看到主物体全部的方体大小
        height = depth;
        width = height * m_camera.aspectRatio;
    }else{
        width = depth;
        height = width / m_camera.aspectRatio;
    }

    m_camera.rightPlane = width / 2.0f;     //近平面可视右面
    m_camera.topPlane = height / 2.0f;     //近平面可视上面
    m_camera.leftPlane = -m_camera.rightPlane;     //近平面可视左面
    m_camera.bottomPlane = -m_camera.topPlane;      //近平面可视下面

    //根据可视角 求近平面 和 远平面
    float tanV = qDegreesToRadians(m_camera.verticalAngle/2.0f);         //垂直方向的可视半角正切值
    tanV = static_cast<float>(qTan(static_cast<qreal>(tanV)));

    m_camera.nearPlane = m_camera.topPlane / tanV;
    m_camera.farPlane = m_camera.nearPlane +  depth;

    // 相机对准
    m_camera.dst = getMainObjCenter();  //目标位置
    m_camera.pos = QVector3D(m_camera.dst.x(),m_camera.dst.y(),
                             m_camera.dst.z() + depth/2 + m_camera.nearPlane
                             );  //相机位置
    m_camera.headV = QVector3D(0.0f,1.0f,0.0f);  //相机顶指向方向向量 (0,1,0)


    //正对主物体顶面时的缩放倍数
    float hr,wr,hobj,wobj,dobj;

    if(m_camera.resetTo == 2){  // 对准YZ平面
        m_camera.rotateMat.rotate(90,1,0,0);
        m_camera.rotateMat.rotate(90,0,1,0);
        dobj = getMainObjWidth();
        wobj = getMainObjHeight();
        hobj = getMainObjDepth();
    }else if(m_camera.resetTo == 1){    //对准XZ平面
        m_camera.rotateMat.rotate(90,1,0,0);
        wobj = getMainObjWidth();
        dobj = getMainObjHeight();
        hobj = getMainObjDepth();
    }else{     // 对准XY平面
        wobj = getMainObjWidth();
        hobj = getMainObjHeight();
        dobj = getMainObjDepth();
    }

    if(m_camera.isOrthoMode){
        wr = height * m_camera.aspectRatio / wobj;
        hr = width / m_camera.aspectRatio / hobj;
    }else{
        hr = m_camera.nearPlane  / (m_camera.nearPlane +  depth/2.0f - dobj/2.0f);

        wr = hr * wobj;
        hr = hr * hobj;
        wr = height * m_camera.aspectRatio / wr;
        hr = width / m_camera.aspectRatio / hr;
    }

    m_camera.basezoom = hr<wr ? hr : wr;    //要较小的值

//    float objW = getMainObjWidth();
//    float objH = getMainObjHeight();

//    //正交投影用的参数
//    if(objH * m_camera.aspectRatio > objW){     //积合宽高比后 求出宽度变大(缩小了) 就使用高度为参考
//        m_camera.topPlane = objH/2.0f;     //正交可视底面
//        m_camera.rightPlane = m_camera.topPlane * m_camera.aspectRatio;      //正交可视左面
//    }else{                                      //放大了 就使用宽度为参考
//        m_camera.rightPlane = objW/2.0f;      //正交可视左面
//        m_camera.topPlane = m_camera.rightPlane / m_camera.aspectRatio;    //正交可视底面
//    }
//    m_camera.leftPlane = -m_camera.rightPlane;     //正交可视右面
//    m_camera.bottomPlane = -m_camera.topPlane;      //正交可视顶面

//    //相机及目标位置，相机方向
//    if(m_camera.verticalAngle<0) m_camera.verticalAngle = -m_camera.verticalAngle;
//    float tanV = qDegreesToRadians(m_camera.verticalAngle/2.0f);         //垂直方向的可视半角正切值
//    tanV = static_cast<float>(qTan(static_cast<qreal>(tanV)));
//    //水平方向的可视半角正切值 宽高比不是正切值的宽高比 而是近平面的宽高比 //float tanH = tanV * m_camera.aspectRatio;不正确

//    objW = 0.5f * objW / m_camera.aspectRatio / tanV;       //从长宽两个值各求近平面距离
//    objH = 0.5f * objH / tanV;
//    objH = objH>objW ? objH : objW;  //用较大的值为近平面的距离

//    m_camera.dst = getMainObjCenter();  //目标位置
//    m_camera.pos = QVector3D(m_camera.dst.x(),m_camera.dst.y(),
//                             m_camera.dst.z() + getMainObjDepth()/2 + objH
//                             );  //相机位置

//    m_camera.headV = QVector3D(0.0f,1.0f,0.0f);  //相机顶指向方向向量 (0,1,0)

//    //通用投影用的参数
//    const float maxZoom = 20.0f;    //  最大放大倍数 (近平面与物体表面重合时)
//    const float minZoom = 0.05f;    //  最小缩小倍数 (远平面与物体表面重合时)

//    m_camera.nearPlane = objH / maxZoom;
//    m_camera.farPlane = objH / minZoom;


}

void GLWidget::resetCameraToXYPlane()
{
    m_camera.resetTo = 0;
    resetCamera();
}

void GLWidget::resetCameraToXZPlane()
{
    m_camera.resetTo = 1;
    resetCamera();
}

void GLWidget::resetCameraToYZPlane()
{
    m_camera.resetTo = 2;
    resetCamera();
}

void GLWidget::setCameraOrtho(bool y)
{
    m_camera.isOrthoMode = y;
}

void GLWidget::setCameraMoveMode(bool afterProjection)
{
    m_camera.moveAfterProjection = afterProjection;
    resetCamera();
}

void GLWidget::setCameraScaleMode(bool scaleCenter)
{
    m_camera.scaleByCenter = scaleCenter;
    update();
}

void GLWidget::setVolumeOpacity(double opacity)
{
    m_volumeCtx.opacity = static_cast<float>(opacity);
//    update();
}

void GLWidget::setVolumeBrightness(double brightness)
{
    m_volumeCtx.brightness = static_cast<float>(brightness);
//    update();
}

void GLWidget::setVolumeThreshold(double threshold)
{
    m_volumeCtx.alphaThreshold = static_cast<float>(threshold);
    //    update();
}

void GLWidget::setVolumeGradient(double gradient)
{
    m_volumeCtx.alphaGradient = static_cast<float>(gradient);
}

void GLWidget::testSurfaceVisible(bool visible)
{
    m_triangleCtx.visible = visible;
    update();
}

QMatrix4x4 GLWidget::getCameraViewMat()
{
    QMatrix4x4 mat,temp;
    QVector3D pos(0.0f,0.0f,0.0f),dst(0.0f,0.0f,0.0f),headV(0.0f,0.0f,0.0f);

    mat.setToIdentity();

    //先旋转相机位置及顶指向方向
    temp.setToIdentity();
    temp = temp * m_camera.rotateMat;

    pos = m_camera.pos - m_camera.dst;  //获取相对位置
    pos = temp * pos;
    headV = temp * m_camera.headV;

//    //相机位置拉近
//    pos = pos / m_camera.zoom;

//    //相机位置及目标位置平移
//    if(m_camera.moveAfterProjection){
//        dst = m_camera.dst;
//    }else{
//        //模型矩阵之后进行平移，就是移动相机位置及视点位置
//        dst = m_camera.dst - m_camera.trans;
//    }
    dst = m_camera.dst;
    pos = pos + dst;  //还原绝对位置
    mat.lookAt(pos,dst,headV);  //lookAt 必须在perspective或ortho矩阵右面相乘

    return mat;
}

QMatrix4x4 GLWidget::getCameraProjectionMat()
{
    QMatrix4x4 mat;

    mat.setToIdentity();

//    //投影矩阵之后(矩阵乘法其实是操作倒置的)为 屏幕平移
//    if(m_camera.moveAfterProjection) mat.translate(m_camera.trans);

    if(!m_camera.isOrthoMode){
        //透视投影
        mat.perspective(m_camera.verticalAngle,m_camera.aspectRatio,
                        m_camera.nearPlane,m_camera.farPlane);
//        mat.perspective(90,m_camera.aspectRatio,
//                        1e-5f,6);
//        mat.frustum()
    }else{
        //正交投影
//        float zoom = m_camera.zoom;
//        if(zoom<1e-5f) zoom = 1e-5f;

//        float leftPlane = m_camera.leftPlane / zoom;
//        float rightPlane = m_camera.rightPlane / zoom;
//        float bottomPlane = m_camera.bottomPlane / zoom;
//        float topPlane = m_camera.topPlane / zoom;

//        mat.ortho(leftPlane,rightPlane,bottomPlane,topPlane,
//                  m_camera.nearPlane,m_camera.farPlane
//                  );

        mat.ortho(m_camera.leftPlane,m_camera.rightPlane,
                  m_camera.bottomPlane,m_camera.topPlane,
                  m_camera.nearPlane,m_camera.farPlane
                  );

    }

    return mat;
}

QMatrix4x4 GLWidget::getCameraScaleTransMat()
{
    QMatrix4x4 mat;

    mat.setToIdentity();

    float zoom =  m_camera.zoom * m_camera.basezoom;

    mat.scale(zoom,zoom);
    mat.translate(m_camera.trans);

    return mat;
}

void GLWidget::setVolume8UC1Data(unsigned char *data, int dwidth, int dheight, int count)
{
    if(!data ||
            (dwidth * dheight * count)<1
            ){
        return;
    }

    int w = dwidth, h = count, d = dheight;     //  体数据帧数与opengl y轴相同向

    m_paintingMutex.lock();

    if(m_volumeCtx.obj3d8UC1Tex) glDeleteTextures(1,&m_volumeCtx.obj3d8UC1Tex);

    glGenTextures(1,&m_volumeCtx.obj3d8UC1Tex);
    glBindTexture(GL_TEXTURE_3D, m_volumeCtx.obj3d8UC1Tex);
    glTexImage3D(GL_TEXTURE_3D, 0, GL_RED,
                 dwidth, dheight, count, 0,
                 GL_RED, GL_UNSIGNED_BYTE,
                 data);

    const float borderColor[] = { 0.0f, 0.0f, 0.0f, 0.0f };
    glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR,
                     borderColor);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); //GL_CLAMP_TO_BORDER
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

//    glTexSubImage3D(GL_TEXTURE_3D,0,
//                    0,0,i,
//                    100,100,1,
//                    GL_RED, GL_UNSIGNED_BYTE,cp
//                    );

    //体数据 原始尺寸 用于光线投射时步进大小计算
    m_volumeCtx.dataXYZ.setX(w);
    m_volumeCtx.dataXYZ.setY(h);  //注意是从Z轴看向原点(体数据顶面)，所以图像数据的高度应设置为Y值
    m_volumeCtx.dataXYZ.setZ(d);

    m_volumeCtx.modelMat.setToIdentity();   //设置模形变换矩阵 使-0.5~0.5尺寸变为 体数据尺寸
    m_volumeCtx.modelMat.scale(w,h,d);
    m_mainObjBoundary.right = w/2.0f;
    m_mainObjBoundary.top = h/2.0f;
    m_mainObjBoundary.front = d/2.0f;
    m_mainObjBoundary.left = -m_mainObjBoundary.right;
    m_mainObjBoundary.bottom = -m_mainObjBoundary.top;
    m_mainObjBoundary.back = -m_mainObjBoundary.front;

    //  不透明面放大设置
    m_triangleCtx.modelMat.setToIdentity();
    m_triangleCtx.modelMat.translate(0.25f*w,0.25f*h,0.25f*d);
    m_triangleCtx.modelMat.scale(w,h,d);

    m_paintingMutex.unlock();
}

void GLWidget::resetVoxelRGBSegmentPoints()
{
    const double grayscaleMaxV = 255.0;
    m_colorfuncRGBPoints.clear();
    m_colorfuncRGBPoints.append(QPair<double,DoubleRGBSZ>
                                (0,DoubleRGBSZ{0,0,0}
                                 ));
    m_colorfuncRGBPoints.append(QPair<double,DoubleRGBSZ>
                                (grayscaleMaxV,DoubleRGBSZ{1,1,1}
                                 ));
}

void GLWidget::addVoxelRGBSegment(double x1, double r1, double g1, double b1, double x2, double r2, double g2, double b2)
{
    m_colorfuncRGBPoints.append(QPair<double,DoubleRGBSZ>
                                (x1,DoubleRGBSZ{r1,g1,b1}
                                 ));
    m_colorfuncRGBPoints.append(QPair<double,DoubleRGBSZ>
                                (x2,DoubleRGBSZ{r2,g2,b2}
                                 ));
}

void GLWidget::clearVoxelRGBSegmentPoints()
{
    m_colorfuncRGBPoints.clear();
}

void GLWidget::modifiedVoxelColorFunc()
{
    const double grayscaleMaxV = 255.0;

    if(m_colorfuncRGBPoints.length()<2) resetVoxelRGBSegmentPoints();
    int cnt = m_colorfuncRGBPoints.length();

    QPair<double,DoubleRGBSZ> s,e;

    for (int j = 0; j < 1024; ++j) {
        double d = j;
        d = d / 1023;
        d = d * grayscaleMaxV;

        m_pColorFuncTexBuf[3 * j] = 0;      //默认值
        m_pColorFuncTexBuf[3 * j + 1] = 0;
        m_pColorFuncTexBuf[3 * j + 2] = 0;

        for (int i = 1; i < cnt; ++i) {
            e = m_colorfuncRGBPoints[i];
            s = m_colorfuncRGBPoints[i-1];
            if(d>=s.first && d<=e.first){
                DoubleRGBSZ t = e.second;
                t.r = t.r - s.second.r;
                t.g = t.g - s.second.g;
                t.b = t.b - s.second.b;

                d = d - s.first;
                d = d / (e.first - s.first);

                t.r = d * t.r + s.second.r;
                t.g = d * t.g + s.second.g;
                t.b = d * t.b + s.second.b;

                m_pColorFuncTexBuf[3 * j] = static_cast<float>(t.r);
                m_pColorFuncTexBuf[3 * j + 1] = static_cast<float>(t.g);
                m_pColorFuncTexBuf[3 * j + 2] = static_cast<float>(t.b);
                break;
            }
        }
//        qDebug()<<m_pColorFuncTexBuf[3 * j]<<m_pColorFuncTexBuf[3 * j + 1]<<m_pColorFuncTexBuf[3 * j + 2];

    }


    m_colorFuncChanged = true;
}

void GLWidget::on_btnGetDepthBufTest1Clicked()
{
    makeCurrent();      //必须要有 因为这个是bing framebuffer之类的操作

    {
        GLint tempi=0;
        glGetFramebufferAttachmentParameteriv(
                    GL_FRAMEBUFFER,
                    GL_DEPTH_ATTACHMENT,
                    GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
                    &tempi
                    );

        //查看openglwidget qt里的深度缓冲区是用什么类型的缓冲区
        qDebug()<<"current fb GL_DEPTH_ATTACHMENT type is GL_RENDERBUFFER? = "<<(tempi == GL_RENDERBUFFER);
        qDebug()<<"current fb GL_DEPTH_ATTACHMENT type is GL_TEXTURE? = "<<(tempi == GL_TEXTURE);

        glGetIntegerv(GL_ALPHA_BITS,&tempi);
        qDebug()<<"GL_ALPHA_BITS = " << tempi;
    }


    GLenum lastReadBuffer;          // 保存当前读取缓冲区的设置
    GLenum lastWriteBuffer;          // 保存当前读取缓冲区的设置

    GLint iViewport[4];         // 视口
    glGetIntegerv(GL_VIEWPORT, iViewport);
    qDebug()<<"view port (w,h) = ( "<<iViewport[2]<<", "<<iViewport[3]<<")";

    glGetIntegerv(GL_READ_BUFFER, reinterpret_cast<GLint *>(&lastReadBuffer));
    glGetIntegerv(GL_DRAW_BUFFER, reinterpret_cast<GLint *>(&lastWriteBuffer));

    //帧缓冲学习测试
    {
        unsigned int framebuffer;
        glGenFramebuffers(1, &framebuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

        int cols=800,rows=600;
        glViewport(0,0,cols,rows);

        // 生成纹理
        unsigned int texColorBuffer;
        glGenTextures(1, &texColorBuffer);
        glBindTexture(GL_TEXTURE_2D, texColorBuffer);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, cols, rows, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); //最后一个参数为null 会使opengl在显卡中新建内存
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); //纹理被放大缩小时的采样插值方式
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        // 将它附加到当前绑定的帧缓冲对象
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texColorBuffer, 0);
        glBindTexture(GL_TEXTURE_2D, 0);

        //关于gl_color_attachment0
        //为了将颜色缓冲区输出到多个缓冲区，着色器必须配置为写入多重颜色输出，
        //从着色器写入颜色输出的一种方法是写入到名为gl_FragData[n]的内建输出中，
        //但不能在同一个着色器中使用gl_FragData[n]和gl_FragColor，n的值是着色器的输出索引。

        //缓冲区映射
        //默认的行为是单独的颜色输出将被发送到颜色绑定0，如果不通知OpenGL如何处理着色器输出，
        //那么只有第一个输出被路由通过。可以通过调用glDrawBuffers来对着色器输出 进行路由，这将覆盖以前所有的映射。
        //下面的代码 相当于 gl_FragData[0]->GL_COLOR_ATTACHMENT0 gl_FragData[1]->GL_COLOR_ATTACHMENT1
        //gl_FragData[2]->GL_COLOR_ATTACHMENT2 gl_FragData[3]不输出
        //路由了多少个GL_COLOR_ATTACHMENT 就要附加多少个附件到帧缓冲对象，否则帧缓冲对象会显示未complete
        GLenum fboBuffers[] = {GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1,GL_COLOR_ATTACHMENT2,GL_NONE};
        glDrawBuffers(4,fboBuffers);

        //glReadPixels 之前调用 glReadBuffer设置读取的目标缓冲区 默认为GL_COLOR_ATTACHMENT0

        // 生成纹理2
        unsigned int texColorBuffer2;
        glGenTextures(1, &texColorBuffer2);
        glBindTexture(GL_TEXTURE_2D, texColorBuffer2);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, cols, rows, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); //最后一个参数为null 会使opengl在显卡中新建内存
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); //纹理被放大缩小时的采样插值方式
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        // 将它附加到当前绑定的帧缓冲对象
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, texColorBuffer2, 0);
        //路由了多少个GL_COLOR_ATTACHMENT 就要附加多少个附件到帧缓冲对象，否则帧缓冲对象会显示未complete
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, texColorBuffer2, 0);
        glBindTexture(GL_TEXTURE_2D, 0);

        // 渲染缓冲对象 是显卡原始格式的数据缓冲
        // 因此它不能在片段着色器中被用作纹理采样读取
        // 但依然可以使用glReadPixels读取内容
        unsigned int rbo;
        glGenRenderbuffers(1, &rbo);
        glBindRenderbuffer(GL_RENDERBUFFER, rbo);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, cols, rows);

        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
                                  GL_RENDERBUFFER, rbo);
        glBindRenderbuffer(GL_RENDERBUFFER, 0);

        // 深度和模板缓冲区的 纹理缓冲方式如下
//        unsigned int texDepthStencilBuffer;
//        glGenTextures(1, &texDepthStencilBuffer);
//        glBindTexture(GL_TEXTURE_2D, texDepthStencilBuffer);
//        glTexImage2D(
//          GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 800, 600, 0,
//          GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr
//        );

//        glFramebufferTexture2D(GL_FRAMEBUFFER,
//                               GL_DEPTH_STENCIL_ATTACHMENT,
//                               GL_TEXTURE_2D, texDepthStencilBuffer, 0);
//        glBindTexture(GL_TEXTURE_2D, 0);


        if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        {
            qDebug() << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!";    //最少要附加一个颜色缓冲附件
        }else{
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            paintGLTriangle();

            GLubyte *pixelsbuf = new GLubyte[ static_cast<size_t>(4 * rows * cols) ];

            //

            //默认情况下，对于glReadPixels函数来说，
            //读取操作在双缓冲区渲染环境下将在后台缓冲区进行，而在单缓冲区渲染环境下则在前台缓冲区进行。
            glReadBuffer(GL_COLOR_ATTACHMENT0);
            glReadPixels(0, 0,
                    cols, rows,
                    GL_RGB,//GL_DEPTH_COMPONENT,
                    GL_UNSIGNED_BYTE, pixelsbuf);  //获取深度缓冲区内容



//            QImage img(cols,rows,QImage::Format_Grayscale8);
//            img.fill(0);
//            for (int i = 0; i < rows; ++i) {
//                for (int j = 0; j < cols; ++j) {
//                    int offset = cols * i + j;
//                    float d = pixelsbuf[offset] * 200;
//                    img.scanLine(rows - i -1)[j] = static_cast<uchar>(d); //不能用img.bits(),由于它的宽度不一定是正确的
//                }
//            }

            QImage img(pixelsbuf,cols,rows,QImage::Format_RGB888);
            img = img.mirrored(false,true);

            img.save("1stFrameBuf.png");

//            glReadBuffer(GL_COLOR_ATTACHMENT1);
//            glReadPixels(0, 0,
//                    cols, rows,
//                    GL_RGB,//GL_DEPTH_COMPONENT,
//                    GL_UNSIGNED_BYTE, pixelsbuf);  //获取深度缓冲区内容

            glBindTexture(GL_TEXTURE_2D,texColorBuffer2);
            glGetTexImage(GL_TEXTURE_2D,0,GL_RGBA,GL_UNSIGNED_BYTE,pixelsbuf);
            glBindTexture(GL_TEXTURE_2D,0);

            img=QImage(pixelsbuf,cols,rows,QImage::Format_RGBA8888);
            img = img.mirrored(false,true);
             qDebug()<<"img frome glGetTexImage GL_COLOR_ATTACHMENT1 center alpha = "
                    << qAlpha(img.pixel(img.width()/2,img.height()/2));

            img.save("1stFrameBufAttach1.png");

            float alphaf[4] = {0.0};
            glReadBuffer(GL_COLOR_ATTACHMENT1);
            glReadPixels(cols/2, rows/2,
                    1, 1,
                    GL_RGBA,//GL_DEPTH_COMPONENT,
                    GL_FLOAT, alphaf);  //获取深度缓冲区内容
            qDebug()<<"GL_COLOR_ATTACHMENT1 center alpha = "<<alphaf[3];


            if(pixelsbuf) delete [] pixelsbuf;
            pixelsbuf = nullptr;
        }

        //恢复原来的帧缓冲区
        //这里解绑不能为0 由于qt是由帧缓冲实现openglwidget,应绑定为默认的帧缓冲
        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
        glDeleteTextures(1,&texColorBuffer);
        glDeleteTextures(1,&texColorBuffer2);
        glDeleteRenderbuffers(1,&rbo);
//            glDeleteTextures(1,&texDepthStencilBuffer);
        glDeleteFramebuffers(1,&framebuffer);

        glViewport(iViewport[0],iViewport[1],iViewport[2],iViewport[3]);
        glReadBuffer(lastReadBuffer);    // 保存当前的设置，后面再恢复它
        fboBuffers[0] = lastWriteBuffer;
        glDrawBuffers(1,fboBuffers);
    }


    //glReadBuffer函数主要是确定颜色缓冲区的来源（不会影响到深度、模板等缓冲区的读取），
    //这里的设置将会影响到glReadPixels, glCopyTexImage1D, glCopyTexImage2D,
    //glCopyTexSubImage1D, glCopyTexSubImage2D, glCopyTexSubImage3D的读取结果。
    //Specifies a color buffer.
    //Accepted values are GL_FRONT_LEFT,
    //GL_FRONT_RIGHT, GL_BACK_LEFT, GL_BACK_RIGHT,
    //GL_FRONT, GL_BACK, GL_LEFT, GL_RIGHT,
    //and the constants GL_COLOR_ATTACHMENTi.
    glReadBuffer(GL_BACK_LEFT); //由于qopenglwidget 是缓冲区实现的 ，以其在类内其实是GL_COLOR_ATTACHMENT0为默认值

    GLfloat *depthbuf = new GLfloat[ static_cast<size_t>(iViewport[2] * iViewport[3]) ];

    //读取深度作为纹理
    {
        //  在OpenGL1.4之后，glCopyTexImage2D允许我们从深度缓冲区中拷贝数据。
        // 纹理 数据多了一种深度纹理的类型，其内部格式包括
        // GL_DEPTH_COMPONENT16,GL_DEPTH_COMPONENT24,GL_DEPTH_COMPONENT32,
        // 数字代表每个纹理单 元包含的位数。一般情况下，我们希望其内部格式与深度缓冲区的精度相匹配。
        // OpenGL允许你指定通用的GL_DEPTH_COMPONENT格式来匹配 你的深度缓冲区。
        GLuint texDepthBuf;
        glGenTextures(1, &texDepthBuf);
        glBindTexture(GL_TEXTURE_2D, texDepthBuf);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
                     iViewport[2], iViewport[3], 0,
                GL_DEPTH_COMPONENT, GL_FLOAT,   //GL_DEPTH_COMPONENT是用于写入源内存的格式
                nullptr); //最后一个参数为null 会使opengl在显卡中新建内存
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); //纹理被放大缩小时的采样插值方式
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
                         0, 0, iViewport[2], iViewport[3],
                         0);

        glGetTexImage(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT,GL_FLOAT,depthbuf);

        glBindTexture(GL_TEXTURE_2D, 0);
        glDeleteTextures(1,&texDepthBuf);

        QImage img(iViewport[2],iViewport[3],QImage::Format_Grayscale8);
        img.fill(0);
        for (int i = 0; i < iViewport[3]; ++i) {
            for (int j = 0; j < iViewport[2]; ++j) {
                int offset = iViewport[2] * i + j;
                float d = depthbuf[offset] * 200;
    //            offset = iViewport[2] * (iViewport[3] - i -1) + j;
                img.scanLine(iViewport[3] - i -1)[j] = static_cast<uchar>(d); //不能用img.bits(),由于它的宽度不一定是正确的
            }
        }

        img.save("1stDepthBuf_CopyTexImage2D.png");
    }

    //默认情况下，对于glReadPixels函数来说，
    //读取操作在双缓冲区渲染环境下将在后台缓冲区进行，而在单缓冲区渲染环境下则在前台缓冲区进行。
    glReadPixels(iViewport[0], iViewport[1],
            iViewport[2], iViewport[3],
            GL_DEPTH_COMPONENT,
            GL_FLOAT, depthbuf);  //获取深度缓冲区内容

    glReadBuffer(lastReadBuffer);    // 保存当前的设置，后面再恢复它

    QImage img(iViewport[2],iViewport[3],QImage::Format_Grayscale8);
    img.fill(0);
    for (int i = 0; i < iViewport[3]; ++i) {
        for (int j = 0; j < iViewport[2]; ++j) {
            int offset = iViewport[2] * i + j;
            float d = depthbuf[offset] * 200;
//            offset = iViewport[2] * (iViewport[3] - i -1) + j;
            img.scanLine(iViewport[3] - i -1)[j] = static_cast<uchar>(d); //不能用img.bits(),由于它的宽度不一定是正确的
        }
    }

    img.save("1stDepthBuf.png");

    if(depthbuf) delete [] depthbuf;
    depthbuf = nullptr;

    doneCurrent();
    update();
}

void GLWidget::on_btnVolumeTestClicked()
{
    const int & width = m_viewportWidth;
    const int & height = m_viewportHeight;

    makeCurrent();

    paintGLVolume();

    glBindFramebuffer(GL_FRAMEBUFFER, m_volumeCtx.fbo);
    glBindTexture(GL_TEXTURE_2D,m_volumeCtx.colortex);
    GLubyte *pixelsbuf = new GLubyte[ static_cast<size_t>(4 * width * height) ];

    //默认情况下，对于glReadPixels函数来说，
    //读取操作在双缓冲区渲染环境下将在后台缓冲区进行，而在单缓冲区渲染环境下则在前台缓冲区进行。
    glReadBuffer(GL_COLOR_ATTACHMENT0);
    glReadPixels(0, 0,
            width, height,
            GL_RGBA,
            GL_UNSIGNED_BYTE, pixelsbuf);  //获取深度缓冲区内容

    QImage img(pixelsbuf,width,height,QImage::Format_RGBA8888);
    img = img.mirrored(false,true);

    img.save("volumeRender.png");

    if(pixelsbuf){
        delete [] pixelsbuf;
        pixelsbuf = nullptr;
    }

    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());

    doneCurrent();
}

void GLWidget::mousePressEvent(QMouseEvent *event)
{
    QOpenGLWidget::mousePressEvent(event);
    m_lastMousePos = event->pos();

    if(event->button()==Qt::LeftButton){
        switch (event->modifiers()) {
        case Qt::NoModifier:
            m_camera_ = m_camera;
            m_cameraTool = CAMCT_PITCH_YAW;
            break;
        case Qt::AltModifier:
            m_camera_ = m_camera;
            m_cameraTool = CAMCT_ROLL;
            break;
        default:
            m_cameraTool = CAMCT_NONE;
            break;
        }
    }else if(event->button()==Qt::MidButton || event->button()==Qt::RightButton){
        if(event->modifiers() == Qt::NoModifier){
            m_lastCameraMat = getCameraProjectionMat() * getCameraViewMat();
            m_camera_ = m_camera;
            m_cameraTool = CAMCT_MOVE;
        }
    }
}

void GLWidget::mouseReleaseEvent(QMouseEvent *event)
{
    QOpenGLWidget::mouseReleaseEvent(event);
     m_cameraTool = CAMCT_NONE;
}

void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
    QOpenGLWidget::mouseMoveEvent(event);
    if(m_cameraTool==CAMCT_PITCH_YAW){
        QPoint pos = event->pos();
        QRect viewrect = QWidget::geometry();
        const float maxrotatdeg = 180.0f;
        float yaw = pos.x() - m_lastMousePos.x();
        float pitch = pos.y() - m_lastMousePos.y();

        yaw = yaw/viewrect.width();
        pitch = pitch/viewrect.height();

        if(yaw<-1.0f) yaw = -1.0f;
        if(pitch<-1.0f) pitch = -1.0f;

        if(yaw>1.0f) yaw = 1.0f;
        if(pitch>1.0f) pitch = 1.0f;

        yaw = maxrotatdeg * yaw;
        pitch = maxrotatdeg * pitch;

        m_camera.rotateMat = m_camera_.rotateMat;
        m_camera.rotateMat.rotate(-yaw,0,1,0);
        m_camera.rotateMat.rotate(-pitch,1,0,0);

        update();
    }else if(m_cameraTool==CAMCT_ROLL){
        QPoint pos = event->pos();
        QRect viewrect = QWidget::geometry();
        const float maxrotatdeg = 180.0f;
        float roll = pos.x() - m_lastMousePos.x();

        roll = roll/viewrect.width();

        if(roll<-1.0f) roll = -1.0f;
        if(roll>1.0f) roll = 1.0f;

        roll = maxrotatdeg * roll;

        m_camera.rotateMat = m_camera_.rotateMat;
        m_camera.rotateMat.rotate(-roll,0,0,1);
        update();
    }else if(m_cameraTool==CAMCT_MOVE){
        QPoint pos = event->pos();
        QRect viewrect = QWidget::geometry();
        float dx = pos.x() - m_lastMousePos.x();
        float dy = pos.y() - m_lastMousePos.y();

//        if(m_camera.moveAfterProjection){
//            //投影矩阵之后移动，计算最终的平移向量，[-1,1]
//            dx = 2.0f * dx/viewrect.width();
//            dy = 2.0f * dy/viewrect.height();
//            m_camera.trans = m_camera_.trans + QVector3D(dx,-dy,0);

//        }else{
//            //模型矩阵之前移动，计算相机位置及视点平移向量
//            dx = 2.0f * dx/viewrect.width();
//            dy = 2.0f * dy/viewrect.height();
//            QVector3D trans(dx,-dy,0);
//            //心须先计算出m_lastCameraMat * m_camera_.trans
//            //不然平移不太正常
//            trans = trans + m_lastCameraMat * m_camera_.trans;
//            trans = m_lastCameraMat.inverted() * trans;
//            m_camera.trans =  trans;
//        }
        dx = 2.0f * dx/viewrect.width() / m_camera.zoom;
        dy = 2.0f * dy/viewrect.height() / m_camera.zoom;
        m_camera.trans = m_camera_.trans + QVector3D(dx,-dy,0) / m_camera.basezoom;
        update();
    }
}

void GLWidget::wheelEvent(QWheelEvent *event)
{
    QOpenGLWidget::wheelEvent(event);
    if(m_cameraTool==CAMCT_NONE){
        int d=event->angleDelta().y();
        float zoom = m_camera.zoom;
        if(d>0){
            zoom = zoom * 1.05f;
            if(zoom>20.f) zoom = 20.f;
        }else if(d<0){
            zoom = zoom * 0.9f;
            if(zoom<0.1f) zoom = 0.1f;
        }

        if(!m_camera.scaleByCenter){ //按鼠标位置缩放
            QRect viewrect = QWidget::geometry();
            QPoint pos = event->pos() - viewrect.center();
            float dx = pos.x();
            float dy = pos.y();
            dx = 2.0f * dx/viewrect.width();
            dy = 2.0f * dy/viewrect.height();

            // yn = a1(x+t1); yn = a2(x+t2);
            // t2 - t1 = yn(1/a2 - 1/a1)
            // t2 = yn(1/a2 - 1/a1) + t1
            dx = dx * (1.0f/zoom - 1.0f/m_camera.zoom) / m_camera.basezoom;
            dy = dy * (1.0f/zoom - 1.0f/m_camera.zoom) / m_camera.basezoom;
            m_camera.trans = m_camera.trans + QVector3D(dx,-dy,0);
        }

        m_camera.zoom = zoom;
        update();
    }
}


void GLWidget::initializeGL()
{
    //    auto glformat = QGLFormat::defaultFormat();
    //    qDebug()<< glformat.majorVersion() << glformat.minorVersion();
    //    qDebug()<< glformat.openGLVersionFlags();

    //    // Set up the rendering context, load shaders and other resources, etc.:
    //    QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();

    //    f->initializeOpenGLFunctions();
    initializeOpenGLFunctions();

    qDebug()<<QString(reinterpret_cast<const char*>(glGetString(GL_VERSION)));

    GLint tempi;
    glGetIntegerv(GL_MAX_DRAW_BUFFERS,&tempi);
    qDebug()<<"glDrawBuffers max = "<<tempi;


    initGLCommonProgrom();
    initGLTriangle();
    initGLVolume();
}

void GLWidget::resizeGL(int w, int h)
{
//    QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();

//    auto f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();

    glViewport(0,0,w,h);
    m_glviewport.setCoords(0,0,w,h);
    m_viewportWidth = w;
    m_viewportHeight = h;

    if(!m_isCamera1stReseted){
        resetCamera();
        m_isCamera1stReseted = true;
    }

//    GLint iViewport[4];         // 视口

//    // 取得当前视口大小
//    glGetIntegerv(GL_VIEWPORT, iViewport);
//    qDebug()<<"view port = ( "<<iViewport[2]<<", "<<iViewport[3]<<")";

    m_isViewportChanged = true;
}

void GLWidget::paintGL()
{
    if(!m_paintingMutex.tryLock())  return;

    glEnable(GL_DEPTH_TEST);
    //深度测试的方案
    //默认是GL_LESS 在片段深度值小于缓冲的深度值时通过测试
    //    GL_ALWAYS 	永远通过深度测试
    //    GL_NEVER 	永远不通过深度测试
    //    GL_LESS 	在片段深度值小于缓冲的深度值时通过测试
    //    GL_EQUAL 	在片段深度值等于缓冲区的深度值时通过测试
    //    GL_LEQUAL 	在片段深度值小于等于缓冲区的深度值时通过测试
    //    GL_GREATER 	在片段深度值大于缓冲区的深度值时通过测试
    //    GL_NOTEQUAL 	在片段深度值不等于缓冲区的深度值时通过测试
    //    GL_GEQUAL 	在片段深度值大于等于缓冲区的深度值时通过测试
    glDepthFunc(GL_LESS);
    glDisable(GL_CULL_FACE);
    glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
    glDepthMask(GL_TRUE);
    glClearColor(0.0f,0.0f,0,1);  //颜色缓冲区清除时的值
    glClearDepth(1.0);      //深度缓冲区清除时的值
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

//    paintGLTest();

    paintGLTriangle();

    paintGLVolume();

//    glFlush();

    // 取得当前视口大小
//    {
//        GLint iViewport[4];         // 视口
//        glGetIntegerv(GL_VIEWPORT, iViewport);
//        qDebug()<<"view port = ( "<<iViewport[2]<<", "<<iViewport[3]<<")";

//        GLfloat zs[10 * 10];
//        glReadPixels(0, 0, 10, 10, GL_DEPTH_COMPONENT, GL_FLOAT, &zs);  //获取深度缓冲区内容
//        zs[0] = zs[0];
//    }

    m_paintingMutex.unlock();
}

float GLWidget::getMainObjMaxSize()
{
    float dx = m_mainObjBoundary.right - m_mainObjBoundary.left;
    float dy = m_mainObjBoundary.top - m_mainObjBoundary.bottom;
    float dz = m_mainObjBoundary.front - m_mainObjBoundary.back;

    return static_cast<float>(qSqrt( static_cast<qreal>(dx*dx + dy*dy + dz*dz) ));
}

QVector3D GLWidget::getMainObjCenter()
{
    float dx = m_mainObjBoundary.right + m_mainObjBoundary.left;
    float dy = m_mainObjBoundary.top + m_mainObjBoundary.bottom;
    float dz = m_mainObjBoundary.front + m_mainObjBoundary.back;

    return QVector3D(dx/2.0f,dy/2.0f,dz/2.0f);
}

float GLWidget::getMainObjWidth()
{
    float dx = m_mainObjBoundary.right - m_mainObjBoundary.left;
    if(dx<0) dx = -dx;
    return dx;
}

float GLWidget::getMainObjHeight()
{
    float dy = m_mainObjBoundary.top - m_mainObjBoundary.bottom;
    if(dy<0) dy = -dy;
    return dy;
}

float GLWidget::getMainObjDepth()
{
    float dz = m_mainObjBoundary.front - m_mainObjBoundary.back;
    if(dz<0) dz = -dz;
    return dz;
}

void GLWidget::initGLCommonProgrom()
{
    QFile file(":/GLSL/common.vert");
    file.open(QIODevice::ReadOnly);

    GLuint vertshader = glCreateShader(GL_VERTEX_SHADER);
    QByteArray code = file.readAll();
    const char * codeptr = code.data();
    GLint len = code.length();
    glShaderSource(vertshader,1,&codeptr,&len);
    glCompileShader(vertshader);
    file.close();
    int  success;
    char infoLog[512];
    glGetShaderiv(vertshader, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        infoLog[0] = 0;
        glGetShaderInfoLog(vertshader, 512, nullptr, infoLog);
        qDebug() << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog;
    }

    file.setFileName(":/GLSL/common.frag");
    file.open(QIODevice::ReadOnly);
    GLuint fragshader = glCreateShader(GL_FRAGMENT_SHADER);
    code = file.readAll();
    codeptr = code.data();
    len = code.length();
    glShaderSource(fragshader,1,&codeptr,&len);
    glCompileShader(fragshader);
    file.close();
    glGetShaderiv(fragshader, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        infoLog[0] = 0;
        glGetShaderInfoLog(fragshader, 512, nullptr, infoLog);
        qDebug() << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog;
    }

    m_glCommonProgram.program = glCreateProgram();
    glAttachShader(m_glCommonProgram.program, vertshader);
    glAttachShader(m_glCommonProgram.program, fragshader);

    glLinkProgram(m_glCommonProgram.program);

    //很多文章说 glGetAttribLocation 是要放在glLinkProgram前面
    //但这里测试是要放在它的后面
    m_glCommonProgram.projViewMat4loc = glGetUniformLocation(m_glCommonProgram.program,"projViewMat");
    qDebug() << "m_glCommonProgram projViewMat uniform location = " << m_glCommonProgram.projViewMat4loc;

    glGetProgramiv(m_glCommonProgram.program, GL_LINK_STATUS, &success);
    if(!success)
    {
        infoLog[0] = 0;
        glGetProgramInfoLog(m_glCommonProgram.program, 512, nullptr, infoLog);
        qDebug() << "ERROR::PROGRAM::LINK_FAILED\n" << infoLog;
    }

    //glLinkProgram后 记得删除着色器对象，我们不再需要它们了
    glDeleteShader(vertshader);
    glDeleteShader(fragshader);
    glUseProgram(0);
}

void GLWidget::initGLTriangle()
{
    //complie and link shader files

//    bool success = m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/GLSL/Triangle/vert");
//    if (!success) {
//        qDebug() << "shaderProgram addShaderFromSourceFile failed!" << m_shaderProgram.log();
//        return;
//    }

//    success = m_shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/GLSL/Triangle/frag");
//    if (!success) {
//        qDebug() << "shaderProgram addShaderFromSourceFile failed!" << m_shaderProgram.log();
//        return;
//    }

//    success = m_shaderProgram.link();
//    if(!success) {
//        qDebug() << "shaderProgram link failed!" << m_shaderProgram.log();
//    }

//    qDebug() << "aPos attribute location = " << m_shaderProgram.attributeLocation("aPos");


    {
        QFile file(m_triangleCtx.vertexCodeFile);
        file.open(QIODevice::ReadOnly);

        GLuint vertshader = glCreateShader(GL_VERTEX_SHADER);
        QByteArray code = file.readAll();
        const char * codeptr = code.data();
        GLint len = code.length();
        glShaderSource(vertshader,1,&codeptr,&len);
        glCompileShader(vertshader);
        file.close();
        int  success;
        char infoLog[512];
        glGetShaderiv(vertshader, GL_COMPILE_STATUS, &success);
        if(!success)
        {
            infoLog[0] = 0;
            glGetShaderInfoLog(vertshader, 512, nullptr, infoLog);
            qDebug() << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog;
        }

        file.setFileName(m_triangleCtx.fragmentCodeFile);
        file.open(QIODevice::ReadOnly);
        GLuint fragshader = glCreateShader(GL_FRAGMENT_SHADER);
        code = file.readAll();
        codeptr = code.data();
        len = code.length();
        glShaderSource(fragshader,1,&codeptr,&len);
        glCompileShader(fragshader);
        file.close();
        glGetShaderiv(fragshader, GL_COMPILE_STATUS, &success);
        if(!success)
        {
            infoLog[0] = 0;
            glGetShaderInfoLog(fragshader, 512, nullptr, infoLog);
            qDebug() << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog;
        }

        m_triangleCtx.program = glCreateProgram();
        glAttachShader(m_triangleCtx.program, vertshader);
        glAttachShader(m_triangleCtx.program, fragshader);

        glLinkProgram(m_triangleCtx.program);

        //很多文章说 glGetAttribLocation 是要放在glLinkProgram前面
        //但这里测试是要放在它的后面
        qDebug() << "aPos attribute location = " << glGetAttribLocation(m_triangleCtx.program,"aPos");
        m_triangleCtx.colorloc = glGetUniformLocation(m_triangleCtx.program,"inputcolor");
        qDebug() << "inputcolor uniform location = " << m_triangleCtx.colorloc;
        m_triangleCtx.projViewMat4loc = glGetUniformLocation(m_triangleCtx.program,"projViewMat");
        qDebug() << "projViewMat uniform location = " << m_triangleCtx.projViewMat4loc;

        glGetProgramiv(m_triangleCtx.program, GL_LINK_STATUS, &success);
        if(!success)
        {
            infoLog[0] = 0;
            glGetProgramInfoLog(m_triangleCtx.program, 512, nullptr, infoLog);
            qDebug() << "ERROR::PROGRAM::LINK_FAILED\n" << infoLog;
        }

        //glLinkProgram后 记得删除着色器对象，我们不再需要它们了
        glDeleteShader(vertshader);
        glDeleteShader(fragshader);

        //设置纹理 uniform 索引号
        //设置一次 着色器程序一直有效
        glUseProgram(m_triangleCtx.program);
        //设置的值对应 glActiveTexture(GL_TEXTUREi)
        glUniform1i(glGetUniformLocation(m_triangleCtx.program,"texHandle1"),0);  //只有一个纹理的情况下 默认就是索引0 不用设置也可以
//        glUniform1i(glGetUniformLocation(m_triangleCtx.program,"texHandle2"),0);  //多个纹理的情况
        glUseProgram(0);

    }


    //triangle VAO，VBO数据部分
    float vertices[] = {
        0.4f,  0.5f, 1.0f,  // top right
        0.5f, -0.5f, 0.0f,  // bottom right
        -0.5f, -0.5f, 0.0f,  // bottom left
        -0.5f,  0.5f, 0.0f   // top left
    };

    m_mainObjBoundary.left = -0.5f;
    m_mainObjBoundary.right = 0.5f;
    m_mainObjBoundary.bottom = -0.5f;
    m_mainObjBoundary.top = 0.5f;
    m_mainObjBoundary.back = 0.0f;
    m_mainObjBoundary.front = 1.0f;

    m_triangleCtx.modelMat.setToIdentity();

    float texcoord[] = {
        0.9f, 1.0f,  // top right
        1.0f, 0.0f,  // bottom right
        0.0f, 0.0f,  // bottom left
        0.0f, 1.0f,  // top left
    };

    unsigned int indices[] = {  // note that we start from 0!
                                0, 1, 3,  // first Triangle
                                1, 2, 3   // second Triangle
                             };


    glGenVertexArrays(1, &m_triangleCtx.vao);
    glGenBuffers(1, &m_triangleCtx.vbo);
    glGenBuffers(1, &m_triangleCtx.ebo);
    // bind the Vertex Array Object first,
    // then bind and set vertex buffer(s),
    // and then configure vertex attributes(s).
    glBindVertexArray(m_triangleCtx.vao);

    glBindBuffer(GL_ARRAY_BUFFER, m_triangleCtx.vbo);
//    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);  //顶点数据复制到缓冲
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices) + sizeof(texcoord),
                 nullptr, GL_STATIC_DRAW);  //data参数为null时 只分配内存
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);                    //缓冲写入部分数据
    glBufferSubData(GL_ARRAY_BUFFER, sizeof(vertices), sizeof(texcoord), texcoord);      //缓冲写入部分数据

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_triangleCtx.ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    //告诉着色器如何解析顶点数据
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat),static_cast<const GLvoid *>(nullptr));
    glEnableVertexAttribArray(0);
    //告诉着色器如何解析纹理点数据
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,
                          2 * sizeof(GLfloat),
                          reinterpret_cast<const GLvoid *>(sizeof(vertices)));
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ARRAY_BUFFER, 0);//取消VBO的绑定, glVertexAttribPointer已经把顶点属性关联到顶点缓冲对象了

    // remember: do NOT unbind the EBO
    // while a VAO is active as the bound element buffer object IS stored in the VAO;
    // keep the EBO bound.
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    //    You can unbind the VAO afterwards
    // so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
    //    VAOs requires a call to glBindVertexArray anyways
    // so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
    glBindVertexArray(0);   //取消VAO绑定


    glGenTextures(1, &m_triangleCtx.texture1);
    glBindTexture(GL_TEXTURE_2D, m_triangleCtx.texture1);
    // 为当前绑定的纹理对象设置环绕、过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   //x方向采样方向越界时的环绕方式为重复
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);   //y方向采样方向越界时的环绕方式为重复
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  //纹理图缩小采样时的插值方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);   //纹理图放大采样时的插值方式

    //加载图像文件
    QImage imgwall(":/Textures/wall.jpg");
    imgwall = imgwall.convertToFormat(QImage::Format_RGB888);   //转换图像的格式为rgb 防止未知格式引起的写入纹理错误
    imgwall = imgwall.mirrored(); //由于opengl纹理原理是在左下角 所以上下翻转一下
    glTexImage2D(GL_TEXTURE_2D,0,
                 GL_RGB,imgwall.width(),imgwall.height(),
                 0,
                 GL_RGB, GL_UNSIGNED_BYTE,
                 nullptr    //imgwall.bits()
                 ); //向纹理写入数据

    for (int i = 0; i < imgwall.height(); ++i) {//这里一行一行复制 防止qimage bits()里有些不是像素的数据
        glTexSubImage2D(GL_TEXTURE_2D,0,
                        0,i,
                        imgwall.width(),1,
                        GL_RGB, GL_UNSIGNED_BYTE,
                        imgwall.scanLine(i)
                        );
    }

//    glGenerateMipmap(GL_TEXTURE_2D); //自动生成远近缩放多级纹理

    glBindTexture(GL_TEXTURE_2D,0); //解绑

    //线框模式，QOpenGLExtraFunctions没这函数, 3_3_Core有
    //    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);


    /*
    QOpenGLVertexArrayObject::Binder vaoBind(&m_triangleVAO);

    m_triangleVBO.create();
    m_triangleVBO.bind();
    m_triangleVBO.allocate(vertices, sizeof(vertices));

    m_triangleEBO.create();
    m_triangleEBO.bind();
    m_triangleEBO.allocate(indices, sizeof(indices));

    int attr = -1;
    attr = m_shaderProgram.attributeLocation("aPos");
    m_shaderProgram.setAttributeBuffer(attr, GL_FLOAT, 0, 3, sizeof(GLfloat) * 3);
    m_shaderProgram.enableAttributeArray(attr);

    m_triangleEBO.release();
    m_triangleVBO.release();
    */
}

void GLWidget::initGLVolume()
{
    //triangle VAO，VBO数据部分
    //以观察点在 (0,0,1)来看 头部上方向向量为 (0,1,0)
    //颜色x轴方向与 顶点的x相同
    //颜色y轴方向与 顶点的z相反
    //颜色z轴方向与 顶点的y相同
    const float vertices[] = {
        //位置                //颜色
        -0.5f,  0.5f, 0.5f,   0.0f, 0.0f, 1.0f, // 前左上角
        -0.5f, -0.5f, 0.5f,   0.0f, 0.0f, 0.0f,  // 前左下角
        0.5f, -0.5f, 0.5f,    1.0f, 0.0f, 0.0f,  // 前右下角
        0.5f,  0.5f, 0.5f,    1.0f, 0.0f, 1.0f,   // 前右上角

        -0.5f,  0.5f, -0.5f,   0.0f, 1.0f, 1.0f, // 后左上角
        -0.5f, -0.5f, -0.5f,   0.0f, 1.0f, 0.0f,  // 后左下角
        0.5f, -0.5f, -0.5f,    1.0f, 1.0f, 0.0f,  // 后右下角
        0.5f,  0.5f, -0.5f,    1.0f, 1.0f, 1.0f,   // 后右上角
    };

    const unsigned int indices[] = {  // note that we start from 0!
                                      0, 1, 2,  // 前面
                                      2, 3, 0,

                                      3, 2, 6,  // 右面
                                      6, 7, 3,

                                      7, 6, 5,  // 后面
                                      5, 4, 7,

                                      4, 5, 0,  // 左面
                                      0, 5, 1,

                                      0, 3, 4,  // 顶面
                                      4, 3, 7,

                                      1, 5, 2,  // 底面
                                      2, 5, 6,

                                   };

    const float vertfill[] = {
        -1,  1, 0,     // 左上角
        -1,  -1, 0,     // 左下角
        1,  -1, 0,     // 右下角

        1,  -1, 0,     // 右下角
        1,  1, 0,     // 右上角
        -1,  1, 0,     // 左上角
    };

//    m_mainObjBoundary.left = -0.5f;
//    m_mainObjBoundary.right = 0.5f;
//    m_mainObjBoundary.bottom = -0.5f;
//    m_mainObjBoundary.top = 0.5f;
//    m_mainObjBoundary.back = -0.5f;
//    m_mainObjBoundary.front = 0.5f;

    glGenVertexArrays(1, &m_volumeCtx.vao);
    glGenBuffers(1, &m_volumeCtx.vbo);
    glGenBuffers(1, &m_volumeCtx.ebo);
    // bind the Vertex Array Object first,
    // then bind and set vertex buffer(s),
    // and then configure vertex attributes(s).
    glBindVertexArray(m_volumeCtx.vao);

    glBindBuffer(GL_ARRAY_BUFFER, m_volumeCtx.vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices),vertices, GL_STATIC_DRAW);  //data参数为null时 只分配内存

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_volumeCtx.ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    //告诉着色器如何解析顶点数据
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat),static_cast<const GLvoid *>(nullptr));
    glEnableVertexAttribArray(0);
    //告诉着色器如何解析颜色数据
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat),reinterpret_cast<const GLvoid *>(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ARRAY_BUFFER, 0);//取消VBO的绑定, glVertexAttribPointer已经把顶点属性关联到顶点缓冲对象了

    // remember: do NOT unbind the EBO
    // while a VAO is active as the bound element buffer object IS stored in the VAO;
    // keep the EBO bound.
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    //    You can unbind the VAO afterwards
    // so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
    //    VAOs requires a call to glBindVertexArray anyways
    // so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
    glBindVertexArray(0);   //取消VAO绑定


    //填满整个屏幕的vao
    //3.x 以上版本 core profile 必须使用 vao
    glGenVertexArrays(1,&m_volumeCtx.vao_fill);
    glBindVertexArray(m_volumeCtx.vao_fill);
    glGenBuffers(1, &m_volumeCtx.vbo_fill);
    glBindBuffer(GL_ARRAY_BUFFER, m_volumeCtx.vbo_fill);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertfill),vertfill, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat),static_cast<const GLvoid *>(nullptr));
    glEnableVertexAttribArray(0);
    glBindVertexArray(0); //取消VAO绑定 不再对此vao进行状态修改

    //初始化灰度彩色映射纹理
    glGenTextures(1,&m_volumeCtx.colormap1dTex);
    glBindTexture(GL_TEXTURE_1D,m_volumeCtx.colormap1dTex);
    glTexImage1D(GL_TEXTURE_1D,0,GL_RGB,
                 1024,0,GL_RGB,GL_FLOAT,nullptr);
    float borderColor[] = { 0.0f, 0.0f, 0.0f, 0.0f };
//    glTexParameterfv(GL_TEXTURE_1D, GL_TEXTURE_BORDER_COLOR,
//                     borderColor);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_1D,0);

    //初始化默认三维体数据
    GLint tempi;
    glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE,&tempi);
    //三维每个维度最大尺寸
    qDebug() << "GL_MAX_3D_TEXTURE_SIZE" << tempi;

    glGenTextures(1,&m_volumeCtx.obj3d8UC1Tex);
    glBindTexture(GL_TEXTURE_3D, m_volumeCtx.obj3d8UC1Tex);
    glTexImage3D(GL_TEXTURE_3D, 0, GL_RED,
                 100, 100, 100, 0,
                 GL_RED, GL_UNSIGNED_BYTE,
                 nullptr);

    glTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR,
                     borderColor);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); //GL_CLAMP_TO_BORDER
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    unsigned char * cp = new unsigned char[100 * 100];
    QPainter painter;
    for (int i = 0; i < 100; ++i) {
        QImage img(cp,100,100,QImage::Format_Grayscale8);
        img.fill(0);
        QPen pen;
        pen.setWidth(3);

        float value = qrand();  //随机灰度值
        value = 155 * value / RAND_MAX;
        value = value + 100;
        int ivalue = static_cast<int>(value);
        pen.setColor(QColor(ivalue,ivalue,ivalue));

        qreal rvalue = 80 * qrand() / RAND_MAX;
        rvalue = rvalue + 20;
        QRectF rectangle(0.0, 0.0, rvalue, rvalue);
        rectangle.moveCenter(QPointF(50,50));

        painter.begin(&img);
        painter.setPen(pen);
        painter.drawArc(rectangle,-15 * 16,195 * 16);
//        painter.drawEllipse(rectangle);
        painter.end();

        glTexSubImage3D(GL_TEXTURE_3D,0,
                        0,0,i,
                        100,100,1,
                        GL_RED, GL_UNSIGNED_BYTE,cp
                        );
//        if(i == 0) img.save("volumedefdata.png");
    }
    delete [] cp;
    cp = nullptr;
    glBindTexture(GL_TEXTURE_3D, 0);

    //体数据 原始尺寸
    m_volumeCtx.dataXYZ.setX(100);
    m_volumeCtx.dataXYZ.setY(100);  //注意是从Z轴看向原点(体数据顶面)，所以图像数据的高度应设置为Y值
    m_volumeCtx.dataXYZ.setZ(100);  //注意是从Z轴看向原点(体数据顶面)，所以图像数据的帧数应设置为Z值

    float w =100,h=200,d=300;
    m_volumeCtx.modelMat.setToIdentity();
    m_volumeCtx.modelMat.scale(w,h,d);
    m_mainObjBoundary.right = w/2.0f;
    m_mainObjBoundary.top = h/2.0f;
    m_mainObjBoundary.front = d/2.0f;
    m_mainObjBoundary.left = -m_mainObjBoundary.right;
    m_mainObjBoundary.bottom = -m_mainObjBoundary.top;
    m_mainObjBoundary.back = -m_mainObjBoundary.front;

    m_triangleCtx.modelMat.setToIdentity();
    m_triangleCtx.modelMat.translate(0.25f*w,0.25f*h,0.25f*d);
    m_triangleCtx.modelMat.scale(w,h,d); 

    //加载编译着色器
    QFile file(":/GLSL/Volume/volume.vert");
    file.open(QIODevice::ReadOnly);

    GLuint vertshader = glCreateShader(GL_VERTEX_SHADER);
    QByteArray code = file.readAll();
    const char * codeptr = code.data();
    GLint len = code.length();
    glShaderSource(vertshader,1,&codeptr,&len);
    glCompileShader(vertshader);
    file.close();
    int  success;
    char infoLog[512];
    glGetShaderiv(vertshader, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        infoLog[0] = 0;
        glGetShaderInfoLog(vertshader, 512, nullptr, infoLog);
        qDebug() << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog;
    }

    file.setFileName(":/GLSL/Volume/volume.frag");
    file.open(QIODevice::ReadOnly);
    GLuint fragshader = glCreateShader(GL_FRAGMENT_SHADER);
    code = file.readAll();
    codeptr = code.data();
    len = code.length();
    glShaderSource(fragshader,1,&codeptr,&len);
    glCompileShader(fragshader);
    file.close();
    glGetShaderiv(fragshader, GL_COMPILE_STATUS, &success);
    if(!success)
    {
        infoLog[0] = 0;
        glGetShaderInfoLog(fragshader, 512, nullptr, infoLog);
        qDebug() << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog;
    }

    m_volumeCtx.program = glCreateProgram();
    glAttachShader(m_volumeCtx.program, vertshader);
    glAttachShader(m_volumeCtx.program, fragshader);

    glLinkProgram(m_volumeCtx.program);

    //很多文章说 glGetAttribLocation 是要放在glLinkProgram前面
    //但这里测试是要放在它的后面
    m_volumeCtx.projViewMat4loc = glGetUniformLocation(m_volumeCtx.program,"projViewMat");
    qDebug() << "m_volumeCtx projViewMat uniform location = " << m_volumeCtx.projViewMat4loc;
    m_volumeCtx.projMatrixLoc = glGetUniformLocation(m_volumeCtx.program,"projMatrix");
    qDebug() << "m_volumeCtx projMatrix uniform location = " << m_volumeCtx.projMatrixLoc;
    m_volumeCtx.renderSteploc = glGetUniformLocation(m_volumeCtx.program,"renderStep");
    qDebug() << "m_volumeCtx renderStep uniform location = " << m_volumeCtx.renderSteploc;
    m_volumeCtx.volumeXYZloc = glGetUniformLocation(m_volumeCtx.program,"volumeXYZ");
    qDebug() << "m_volumeCtx volumeXYZ uniform location = " << m_volumeCtx.volumeXYZloc;
    m_volumeCtx.viewportWHloc = glGetUniformLocation(m_volumeCtx.program,"viewportWH");
    qDebug() << "m_volumeCtx viewportWH uniform location = " << m_volumeCtx.viewportWHloc;
    m_volumeCtx.opacityLoc = glGetUniformLocation(m_volumeCtx.program,"opacity");
    m_volumeCtx.brightnessLoc = glGetUniformLocation(m_volumeCtx.program,"brightness");
    m_volumeCtx.alphaThresholdLoc = glGetUniformLocation(m_volumeCtx.program,"alphaThreshold");
    m_volumeCtx.alphaGradientLoc = glGetUniformLocation(m_volumeCtx.program,"alphaGradient");

    glGetProgramiv(m_volumeCtx.program, GL_LINK_STATUS, &success);
    if(!success)
    {
        infoLog[0] = 0;
        glGetProgramInfoLog(m_volumeCtx.program, 512, nullptr, infoLog);
        qDebug() << "ERROR::PROGRAM::LINK_FAILED\n" << infoLog;
    }

    //glLinkProgram后 记得删除着色器对象，我们不再需要它们了
    glDeleteShader(vertshader);
    glDeleteShader(fragshader);

    glUseProgram(m_volumeCtx.program);

    //glUniform必须在glUseProgram状态下设置
    // 下面的代码会告诉你单个shader中可以最多访问多少个纹理单元。
    // 为了把纹理对象绑定到某一个纹理单元，你需要把glBindTexture替换成glBindTextureUnit，
    // 它的函数申明如下：
    glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &tempi);
    qDebug() << "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = " << tempi;
    //设置的值对应 glActiveTexture(GL_TEXTUREi)
    //只有一个纹理的情况下 默认就是索引0 不用设置也可以
    m_volumeCtx.bottomTexUnit = GL_TEXTURE0;
    m_volumeCtx.volumeDataTexUnit = GL_TEXTURE1;
    m_volumeCtx.colormap1dTexUnit = GL_TEXTURE2;
    m_volumeCtx.bgColorTexUnit = GL_TEXTURE3;
    m_volumeCtx.bgDepthTexUnit = GL_TEXTURE4;

    glUniform1i(glGetUniformLocation(m_volumeCtx.program,"bottomTex"),
                m_volumeCtx.bottomTexUnit - GL_TEXTURE0);       //必须在glUseProgram之后 否则无效
    glUniform1i(glGetUniformLocation(m_volumeCtx.program,"volumeDataTex"),
                m_volumeCtx.volumeDataTexUnit - GL_TEXTURE0);
    glUniform1i(glGetUniformLocation(m_volumeCtx.program,"colorMapTex"),
                m_volumeCtx.colormap1dTexUnit - GL_TEXTURE0);
    glUniform1i(glGetUniformLocation(m_volumeCtx.program,"bgColorTex"),
                m_volumeCtx.bgColorTexUnit - GL_TEXTURE0);
    glUniform1i(glGetUniformLocation(m_volumeCtx.program,"bgDepthTex"),
                m_volumeCtx.bgDepthTexUnit - GL_TEXTURE0);
    tempi = 0;
    glGetUniformiv(m_volumeCtx.program,glGetUniformLocation(m_volumeCtx.program,"bottomTex"),&tempi);
    qDebug()<< "bottomTex locate in texure uint" <<tempi;
    tempi = 0;
    glGetUniformiv(m_volumeCtx.program,glGetUniformLocation(m_volumeCtx.program,"volumeDataTex"),&tempi);
    qDebug()<< "volumeDataTex locate in texure uint" <<tempi;
    tempi = 0;
    glGetUniformiv(m_volumeCtx.program,glGetUniformLocation(m_volumeCtx.program,"colorMapTex"),&tempi);
    qDebug()<< "colorMapTex locate in texure uint" <<tempi;
    tempi = 0;
    glGetUniformiv(m_volumeCtx.program,glGetUniformLocation(m_volumeCtx.program,"bgColorTex"),&tempi);
    qDebug()<< "bgColorTex locate in texure uint" <<tempi;
    tempi = 0;
    glGetUniformiv(m_volumeCtx.program,glGetUniformLocation(m_volumeCtx.program,"bgDepthTex"),&tempi);
    qDebug()<< "bgDepthTex locate in texure uint" <<tempi;

    glUseProgram(0);
}

bool GLWidget::reallocateVolumeTexture()
{
    if(!m_isViewportChanged) return true;

    if(m_volumeCtx.bgColorTex > 0) glDeleteTextures(1, &m_volumeCtx.bgColorTex);
    if(m_volumeCtx.objbottomTex > 0) glDeleteTextures(1, &m_volumeCtx.objbottomTex);
    if(m_volumeCtx.depthtex > 0) glDeleteTextures(1, &m_volumeCtx.depthtex);
    if(m_volumeCtx.colortex > 0) glDeleteTextures(1, &m_volumeCtx.colortex);
    if(m_volumeCtx.bgDepthTex > 0) glDeleteTextures(1, &m_volumeCtx.bgDepthTex);
    if(m_volumeCtx.rbo > 0) glDeleteRenderbuffers(1, &m_volumeCtx.rbo);
    if(m_volumeCtx.fbo > 0) glDeleteFramebuffers(1, &m_volumeCtx.fbo);

    m_volumeCtx.fbo = 0;
    m_volumeCtx.rbo = 0;
    m_volumeCtx.bgDepthTex = 0;
    m_volumeCtx.colortex = 0;
    m_volumeCtx.depthtex = 0;
    m_volumeCtx.objbottomTex = 0;
    m_volumeCtx.bgColorTex = 0;

    const int & width = m_viewportWidth;
    const int & height = m_viewportHeight;

    glGenFramebuffers(1, &m_volumeCtx.fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, m_volumeCtx.fbo);

    GLuint h[6] = {0};
    h[0] = m_volumeCtx.bgDepthTex;
    h[1] = m_volumeCtx.depthtex;
    for (int i = 0; i < 2; ++i) {
        glGenTextures(1, &h[i]);
        glBindTexture(GL_TEXTURE_2D, h[i]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
                     width, height, 0,
                     GL_DEPTH_COMPONENT, GL_FLOAT,   //GL_DEPTH_COMPONENT是用于写入源内存的格式
                     nullptr);                  //最后一个参数为null 会使opengl在显卡中新建内存
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glBindTexture(GL_TEXTURE_2D, 0);
    }
    m_volumeCtx.bgDepthTex = h[0];
    m_volumeCtx.depthtex = h[1];

//    glGenTextures(1, &m_volumeCtx.depthtex);
//    glBindTexture(GL_TEXTURE_2D, m_volumeCtx.depthtex);
//    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8,
//                 width, height, 0,
//                 GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8,
//                 nullptr);                  //最后一个参数为null 会使opengl在显卡中新建内存
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//    glBindTexture(GL_TEXTURE_2D, 0);

    h[0] = m_volumeCtx.colortex;
    h[1] = m_volumeCtx.objbottomTex;
    h[2] = m_volumeCtx.bgColorTex;
    for (int i = 0; i < 3; ++i) {
        glGenTextures(1, &h[i]);
        glBindTexture(GL_TEXTURE_2D, h[i]);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F,
                     width, height, 0,
                     GL_RGBA, GL_FLOAT, // 这里不能为 GL_RGBA32F 否则不会创建显存
                     nullptr);
        float borderColor[] = { 0.0f, 0.0f, 0.0f, 0.0f };
        glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR,
                         borderColor);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);  //GL_CLAMP_TO_BORDER
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);  //GL_CLAMP_TO_BORDER
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glBindTexture(GL_TEXTURE_2D, 0);
    }
    m_volumeCtx.colortex = h[0];
    m_volumeCtx.objbottomTex = h[1];
    m_volumeCtx.bgColorTex = h[2];

    glBindTexture(GL_TEXTURE_2D, m_volumeCtx.colortex);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                           GL_TEXTURE_2D, m_volumeCtx.colortex, 0);

//    glBindTexture(GL_TEXTURE_2D, m_volumeCtx.depthtex);
//    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
//                           GL_TEXTURE_2D, m_volumeCtx.depthtex, 0);
    glBindTexture(GL_TEXTURE_2D, 0);

//    GLuint tempui=0;        //
//    glGetFramebufferAttachmentParameteriv(
//                GL_FRAMEBUFFER,
//                GL_STENCIL_ATTACHMENT,
//                GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
//                reinterpret_cast<GLint *>(&tempui) //结果为0
//                );


    glGenRenderbuffers(1, &m_volumeCtx.rbo);
    glBindRenderbuffer(GL_RENDERBUFFER, m_volumeCtx.rbo);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); //分配显存

    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
                              GL_RENDERBUFFER, m_volumeCtx.rbo);    //附加到帧缓冲

    glBindRenderbuffer(GL_RENDERBUFFER, 0);
    glBindTexture(GL_TEXTURE_2D, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());

    m_isViewportChanged = false;

    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        qDebug() << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!";    //最少要附加一个颜色缓冲附件
        return false;
    }else{
        return true;
    }
}

void GLWidget::paintGLTest()
{
//    GLfloat vertfill[] = {
//        -1,  1, 0,     // 左上角
//        -1,  -1, 0,     // 左下角
//        1,  -1, 0,     // 右下角

//        1,  -1, 0,     // 右下角
//        1,  1, 0,     // 右上角
//        -1,  1, 0,     // 左上角
//    };

    glUseProgram(m_glCommonProgram.program);

    glClearDepth(1.0);
//    glDepthFunc(GL_LESS);
//    glClear(GL_DEPTH_BUFFER_BIT);

//    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
//    glDrawBuffer(GL_COLOR_ATTACHMENT0);

    QMatrix4x4 m;
    m.setToIdentity();
//    m.scale(0.5,0.5,0.5);
    glUniformMatrix4fv(m_glCommonProgram.projViewMat4loc,1,GL_FALSE,m.data());


    //// 3.x core profile(配置) 不使用vao 根本画不出东西
//    glDisableVertexAttribArray(1);
//    glDisableVertexAttribArray(0);
//    glBindBuffer(GL_ARRAY_BUFFER, 0);
////    glBindBuffer(GL_ARRAY_BUFFER, m_volumeCtx.vbo_fill);
////    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3,vertfill);
////    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,3,vertfill);
//    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertfill);
//    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, vertfill);
//    glEnableVertexAttribArray(0);
//    glEnableVertexAttribArray(1);

//    glUseProgram(m_glCommonProgram.program);
//    glDrawArrays(GL_TRIANGLES,0,6);
//    glBindBuffer(GL_ARRAY_BUFFER, 0);



//    GLint temp = 0;
////     glBindBuffer(GL_ARRAY_BUFFER, m_volumeCtx.vbo_fill);
//    glGetIntegerv(GL_ARRAY_BUFFER_BINDING, & temp);
//    qDebug()<<"GL_ARRAY_BUFFER_BINDING = "<<temp;

//    glBindVertexArray(0);
//    glBindBuffer(GL_ARRAY_BUFFER, 0);
//    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
//    glActiveTexture(GL_TEXTURE0);
//    glBindTexture(GL_TEXTURE_2D,0);

//    glBindVertexArray(m_triangleCtx.vao);

//    glBindBuffer(GL_ARRAY_BUFFER, m_volumeCtx.vbo_fill);
//    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3 * sizeof(float),nullptr);
//    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,3 * sizeof(float),nullptr);
//    glEnableVertexAttribArray(0);
//    glEnableVertexAttribArray(1);

    glBindVertexArray(m_volumeCtx.vao_fill);
    glDrawArrays(GL_TRIANGLES,0,6);
    glBindVertexArray(0);
//    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_triangleCtx.ebo);
//    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, static_cast<const GLvoid *>(nullptr));

//    glDisableVertexAttribArray(0);
//    glDisableVertexAttribArray(1);
//    glBindBuffer(GL_ARRAY_BUFFER, 0);
//    qDebug() << GetLastError();
    glUseProgram(0);
}

void GLWidget::paintGLTriangle()
{
    if(!m_triangleCtx.visible) return;

//    m_shaderProgram.bind();
    glUseProgram(m_triangleCtx.program);

    glBindVertexArray(m_triangleCtx.vao); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
//    glDrawArrays(GL_TRIANGLES, 0, 6);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_triangleCtx.ebo);

    //激活并绑定纹理 如果只有一个纹理  默认是不用激活GL_TEXTURE0的
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,m_triangleCtx.texture1);
    //多个纹理的情况
//    glActiveTexture(GL_TEXTURE1);
//    glBindTexture(GL_TEXTURE_2D,m_triangleCtx.texture2);

    glUniform3f(m_triangleCtx.colorloc,1.0f, 0.5f, 0.2f);

//    m_camera.zoom = 1.0f;
//    m_camera.pitch = 45.0f;
//    m_camera.yaw = 45.0f;
//    m_camera.roll = 45.0f;
//    m_camera.trans.setX(0.5);
    QMatrix4x4 m = getCameraScaleTransMat() * getCameraProjectionMat() * getCameraViewMat();
    m = m * m_triangleCtx.modelMat;
//    m.setToIdentity();
//    m.rotate(45,1,0,0);
//    m.rotate(45,0,1,0);
//    m.rotate(45,0,0,1);
//    qDebug() << m * QVector3D(0.4f,  0.5f, 1.0f);
//    m.setToIdentity();
//    m.translate(0.5f,0.5f);
    glUniformMatrix4fv(m_triangleCtx.projViewMat4loc,1,GL_FALSE,m.data());

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, static_cast<const GLvoid *>(nullptr));

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,0);

    glBindVertexArray(0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);



    /*
    {
        QOpenGLVertexArrayObject::Binder vaoBind(&m_triangleVAO);
        m_triangleEBO.bind();
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, static_cast<const GLvoid *>(0));
        m_triangleEBO.release();
    }
    */

    glUseProgram(0);
    //    m_shaderProgram.release();
}

void GLWidget::paintGLVolume()
{

    if(!reallocateVolumeTexture()) return;
    const int & width = m_viewportWidth;
    const int & height = m_viewportHeight;

    GLuint defcolortex=0,defdetphtex=0,defdepthatttype = 0;        //默认帧缓冲区的颜色缓冲
    glGetFramebufferAttachmentParameteriv(
                GL_FRAMEBUFFER,
                GL_COLOR_ATTACHMENT0,
                GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
                reinterpret_cast<GLint *>(&defcolortex)
                );

    glGetFramebufferAttachmentParameteriv(
                GL_FRAMEBUFFER,
                GL_DEPTH_ATTACHMENT,
                GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
                reinterpret_cast<GLint *>(&defdetphtex)
                );

    glGetFramebufferAttachmentParameteriv(
                GL_FRAMEBUFFER,
                GL_DEPTH_ATTACHMENT,
                GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
                reinterpret_cast<GLint *>(&defdepthatttype)
                );

    //复制体绘制前的背景颜色和深度缓冲
    glReadBuffer(GL_COLOR_ATTACHMENT0);
    glBindTexture(GL_TEXTURE_2D, m_volumeCtx.bgColorTex);  //颜色缓冲
    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
                     0, 0, width, height,
                     0);
    glReadBuffer(GL_DEPTH_ATTACHMENT);
    glBindTexture(GL_TEXTURE_2D, m_volumeCtx.bgDepthTex);   //深度缓冲
    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
                     0, 0, width, height,
                     0);
    glBindTexture(GL_TEXTURE_2D, 0);

    if(m_colorFuncChanged){     //写入新的灰度转彩色映射纹理
        glBindTexture(GL_TEXTURE_1D,m_volumeCtx.colormap1dTex);
        glTexImage1D(GL_TEXTURE_1D,0,GL_RGB,
                     1024,0,
                     GL_RGB,GL_FLOAT,m_pColorFuncTexBuf
                     );
        glBindTexture(GL_TEXTURE_1D,0);
        m_colorFuncChanged = false;
    }

    GLenum lastReadBuffer;          // 保存当前读取缓冲区的设置
    GLenum lastWriteBuffer;          // 保存当前读取缓冲区的设置
    glGetIntegerv(GL_READ_BUFFER, reinterpret_cast<GLint *>(&lastReadBuffer));
    glGetIntegerv(GL_DRAW_BUFFER, reinterpret_cast<GLint *>(&lastWriteBuffer));

    //切换到自定义的帧缓冲
    glBindFramebuffer(GL_FRAMEBUFFER,m_volumeCtx.fbo);
    glDrawBuffer(GL_COLOR_ATTACHMENT0);

//    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    glUseProgram(m_volumeCtx.program);

    glBindVertexArray(m_volumeCtx.vao);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_volumeCtx.ebo);

    GLint renderstep = 1;
    QMatrix4x4 m = getCameraScaleTransMat() * getCameraProjectionMat() * getCameraViewMat();
    m = m * m_volumeCtx.modelMat;

    //绘画底面
    renderstep = 1;
    glClearDepth(0.0);
    glClearColor(0,0,0,0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);
    glDepthFunc(GL_GEQUAL); //只画大于等于深度缓冲的部分
    glUniform1i(m_volumeCtx.renderSteploc,renderstep);
    glUniformMatrix4fv(m_volumeCtx.projViewMat4loc,1,GL_FALSE,m.data());
    glDrawElements(GL_TRIANGLES, 6 * 6, GL_UNSIGNED_INT, static_cast<const GLvoid *>(nullptr));
//    //底部结果复制到底部纹理
//    glReadBuffer(GL_COLOR_ATTACHMENT0);
//    glBindTexture(GL_TEXTURE_2D, m_volumeCtx.objbottomTex);
//    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F,
//                     0, 0, width, height,
//                     0);
//    glBindTexture(GL_TEXTURE_2D, 0);


    //绘制顶面 计算光线投射结果
    //直接绘制到默认帧缓冲
    glDrawBuffer(lastWriteBuffer);
    glReadBuffer(lastReadBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());  //恢复默认的缓冲区
    renderstep = 2;
    glActiveTexture(static_cast<GLenum>(m_volumeCtx.colormap1dTexUnit));
//    glEnable(GL_TEXTURE_1D);
    glBindTexture(GL_TEXTURE_1D,m_volumeCtx.colormap1dTex);  //设置纹理2
    glActiveTexture(static_cast<GLenum>(m_volumeCtx.bottomTexUnit));
//    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D,m_volumeCtx.colortex);  //设置纹理0 m_volumeCtx.objbottomTex m_volumeCtx.colortex
    glActiveTexture(static_cast<GLenum>(m_volumeCtx.bgColorTexUnit));
    glBindTexture(GL_TEXTURE_2D,m_volumeCtx.bgColorTex);  //设置纹理3
    glActiveTexture(static_cast<GLenum>(m_volumeCtx.bgDepthTexUnit));
    glBindTexture(GL_TEXTURE_2D,m_volumeCtx.bgDepthTex);  //设置纹理4
    glActiveTexture(static_cast<GLenum>(m_volumeCtx.volumeDataTexUnit));
//    glEnable(GL_TEXTURE_3D);
    glBindTexture(GL_TEXTURE_3D,m_volumeCtx.obj3d8UC1Tex); //设置纹理1
//    glClearDepth(1.0);
//    glClearColor(0,0,0,0);
//    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_CULL_FACE);
    glDepthFunc(GL_LESS); //只画小于深度缓冲的部分
    glUniform1i(m_volumeCtx.renderSteploc,renderstep);
    glUniformMatrix4fv(m_volumeCtx.projViewMat4loc,1,GL_FALSE,m.data());
    m = getCameraScaleTransMat() * getCameraProjectionMat();
    glUniformMatrix4fv(m_volumeCtx.projMatrixLoc,1,GL_FALSE,
                       m.data());
    glUniform3f(m_volumeCtx.volumeXYZloc,
                m_volumeCtx.dataXYZ[0],m_volumeCtx.dataXYZ[1],m_volumeCtx.dataXYZ[2]);
    glUniform2f(m_volumeCtx.viewportWHloc, m_viewportWidth, m_viewportHeight);
    glUniform1f(m_volumeCtx.opacityLoc,m_volumeCtx.opacity);
    glUniform1f(m_volumeCtx.brightnessLoc,m_volumeCtx.brightness);
    glUniform1f(m_volumeCtx.alphaThresholdLoc,m_volumeCtx.alphaThreshold);
    glUniform1f(m_volumeCtx.alphaGradientLoc,m_volumeCtx.alphaGradient);

    glDrawElements(GL_TRIANGLES, 6 * 6, GL_UNSIGNED_INT, static_cast<const GLvoid *>(nullptr));

    glActiveTexture(static_cast<GLenum>(m_volumeCtx.bottomTexUnit));
    glBindTexture(GL_TEXTURE_2D,0);  //解绑纹理0
    glActiveTexture(static_cast<GLenum>(m_volumeCtx.volumeDataTexUnit));
    glBindTexture(GL_TEXTURE_3D,0);  //解绑纹理1
    glActiveTexture(static_cast<GLenum>(m_volumeCtx.colormap1dTexUnit));
    glBindTexture(GL_TEXTURE_1D,0);  //解绑纹理2
    glActiveTexture(static_cast<GLenum>(m_volumeCtx.bgColorTexUnit));
    glBindTexture(GL_TEXTURE_2D,0);  //设置纹理3
    glActiveTexture(static_cast<GLenum>(m_volumeCtx.bgDepthTexUnit));
    glBindTexture(GL_TEXTURE_2D,0);  //设置纹理4
    glBindVertexArray(0); //解绑vao
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    glUseProgram(0);        //取消使用体绘制着色程序

//    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//    glEnable(GL_DEPTH_TEST);
//    glDepthFunc(GL_LESS);

//   // 将绘制混合后的结果复制的默认的颜色缓冲区
//    glReadBuffer(GL_COLOR_ATTACHMENT0);
//    glBindTexture(GL_TEXTURE_2D, defcolortex);
//    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F,
//                     0, 0, width, height,
//                     0);
//    glBindTexture(GL_TEXTURE_2D, 0);

    //恢复默认的缓冲区
    glDrawBuffer(lastWriteBuffer);
    glReadBuffer(lastReadBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());


}
